package com.kongzue.baseokhttp;


import com.kongzue.baseokhttp.listener.WebSocketStatusListener;
import com.kongzue.baseokhttp.util.BaseOkHttp;
import com.kongzue.baseokhttp.util.BaseOkHttpConfig;
import com.kongzue.baseokhttp.util.LockLog;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySlice;
import ohos.app.Context;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.bundle.AbilityInfo;
import ohos.net.NetHandle;
import ohos.net.NetManager;
import okhttp3.*;
import okio.ByteString;

import java.lang.ref.WeakReference;


/**
 * Author: @Kongzue Github: https://github.com/kongzue/ Homepage: http://kongzue.com/ Mail:
 * myzcxhh@live.cn CreateTime: 2018/12/31 17:20 reDesign
 * from @WinRoad(https://github.com/hhh5022456)
 */
public class BaseWebSocket {

    /**
     * 连接中
     */
    public final static int CONNECTING = 0;

    /**
     * 已连接
     */
    public static final int CONNECTED = 1;

    /**
     * 重连中
     */
    public static final int RECONNECT = 2;
    /**
     * 断开
     */
    public static final int DISCONNECTED = -1;
    /**
     * 正常断开
     */
    public static final int BREAK_NORMAL = 0;
    /**
     * 异常断开
     */
    public static final int BREAK_ABNORMAL = 1;

    private WeakReference<Context> context;

    private String url;

    private WebSocket webSocket;

    private OkHttpClient okHttpClient;

    private Request request;

    private int status = DISCONNECTED;

    /**
     * 自动重连
     */
    private boolean autoReconnect = true;

    /**
     * 手动关闭
     */
    private boolean manualClose = false;

    private WebSocketStatusListener webSocketStatusListener;

    /**
     * 重连次数
     */
    private int reconnectCount = 0;

    private TaskDispatcher mainLooperHandler;

    /**
     * 创建方法
     *
     * @param context context
     * @param url     url
     * @return BaseWebSocket
     */
    public static BaseWebSocket BUILD(Context context, String url) {
        synchronized (BaseWebSocket.class) {
            BaseWebSocket baseWebSocket = new BaseWebSocket();
            baseWebSocket.context = new WeakReference<Context>(context);
            baseWebSocket.url = url;
            return baseWebSocket;
        }
    }

    /**
     * 创建方法
     *
     * @param context      context
     * @param url          url
     * @param okHttpClient okHttpClient
     * @return BaseWebSocket
     */
    public static BaseWebSocket BUILD(Context context, String url, OkHttpClient okHttpClient) {
        synchronized (BaseWebSocket.class) {
            BaseWebSocket baseWebSocket = new BaseWebSocket();
            baseWebSocket.context = new WeakReference<Context>(context);
            baseWebSocket.url = url;
            baseWebSocket.okHttpClient = okHttpClient;
            return baseWebSocket;
        }
    }

    private void doConnect() {
        if (okHttpClient == null) {
            okHttpClient = new OkHttpClient.Builder().retryOnConnectionFailure(true).build();
        }
        if (request == null) {
            request = new Request.Builder().url(url).build();
        }

        if (mainLooperHandler == null && context != null && context.get() != null) {
            mainLooperHandler = context.get().getMainTaskDispatcher();
        }

        okHttpClient.dispatcher().cancelAll();
        okHttpClient.newWebSocket(request, webSocketListener);
    }

    /**
     * 开始连接
     *
     * @return BaseWebSocket
     */
    public BaseWebSocket startConnect() {
        manualClose = false;
        buildConnect();
        return this;
    }

    private void buildConnect() {
        if (!isNetworkConnected(context.get())) {
            status = DISCONNECTED;
        }
        if (status != CONNECTED && status != CONNECTING) {
            status = CONNECTING;
            doConnect();
        }
    }

    /**
     * 尝试重新连接
     */
    public void reConnect() {
        manualClose = true;
        if (!autoReconnect | manualClose) {
            return;
        }
        if (BaseOkHttpConfig.getInstance().isDebugMode()) {
            loge("重连次数：" + reconnectCount);
        }
        if (!isNetworkConnected(context.get())) {
            status = DISCONNECTED;
            loge("网络错误");
        }

        status = RECONNECT;
        long delay = reconnectCount * BaseOkHttpConfig.getInstance().getWebsocketReconnectInterval() * 1000;
        mainLooperHandler.delayDispatch(reconnectRunnable, delay);
        reconnectCount++;
    }

    private Runnable reconnectRunnable = new Runnable() {
        @Override
        public void run() {
            if (webSocketStatusListener != null) {
                webSocketStatusListener.onReconnect();
            }
            buildConnect();
        }
    };

    /**
     * 断开连接
     */
    public void disConnect() {
        if (status == DISCONNECTED) {
            return;
        }
        if (okHttpClient != null) {
            okHttpClient.dispatcher().cancelAll();
        }
        if (webSocket != null) {
            try {
                boolean isClosed = webSocket.close(1000, "normal close");

                // 非正常关闭连接
                if (!isClosed) {
                    if (webSocketStatusListener != null) {
                        webSocketStatusListener.onDisconnected(BREAK_ABNORMAL);
                    }
                }
            } catch (Exception e) {
                if (webSocketStatusListener != null) {
                    webSocketStatusListener.onDisconnected(BREAK_ABNORMAL);
                }
            }
        }
        status = DISCONNECTED;
    }

    /**
     * 发送消息
     *
     * @param msg msg
     * @return boolean
     */
    public boolean send(String msg) {
        return sendMsg(msg);
    }

    /**
     * send
     *
     * @param byteString byteString
     * @return boolean
     */
    public boolean send(ByteString byteString) {
        return sendMsg(byteString);
    }

    private boolean sendMsg(Object msg) {
        boolean isSend = false;
        if (webSocket != null && status == CONNECTED) {
            if (msg instanceof String) {
                isSend = webSocket.send((String) msg);
            } else if (msg instanceof ByteString) {
                isSend = webSocket.send((ByteString) msg);
            }

            // 发送消息失败，尝试重连
            if (!isSend) {
                loge("发送失败，尝试重连...");
                reConnect();
            }
        } else {
            loge("未建立连接，无法发送消息");
        }
        return isSend;
    }

    private WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onOpen(WebSocket ws, final Response response) {
            logd("已建立连接");
            webSocket = ws;
            status = CONNECTED;
            reconnectCount = 0;
            if (webSocketStatusListener != null) {
                if (isMainThread()) {
                    mainLooperHandler.syncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            webSocketStatusListener.connected(response);
                        }
                    });
                } else {
                    webSocketStatusListener.connected(response);
                }
            }
        }

        @Override
        public void onMessage(WebSocket webSocket, final String text) {
            logd("接收到消息:" + text);
            if (webSocketStatusListener != null) {
                if (isMainThread()) {
                    mainLooperHandler.syncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            webSocketStatusListener.onMessage(text);
                        }
                    });
                } else {
                    webSocketStatusListener.onMessage(text);
                }
            }
        }

        @Override
        public void onMessage(WebSocket webSocket, final ByteString bytes) {
            logd("接收到消息:" + bytes);
            if (webSocketStatusListener != null) {
                if (isMainThread()) {
                    mainLooperHandler.syncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            webSocketStatusListener.onMessage(bytes);
                        }
                    });
                } else {
                    webSocketStatusListener.onMessage(bytes);
                }
            }
        }

        @Override
        public void onClosing(WebSocket webSocket, final int code, String reason) {
            logd("连接正在断开:" + code);
        }

        @Override
        public void onClosed(WebSocket webSocket, final int code, String reason) {
            logd("连接已断开:" + code);
            if (webSocketStatusListener != null) {
                if (isMainThread()) {
                    mainLooperHandler.syncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            webSocketStatusListener.onDisconnected(BREAK_NORMAL);
                        }
                    });
                } else {
                    webSocketStatusListener.onDisconnected(BREAK_NORMAL);
                }
            }
        }

        @Override
        public void onFailure(WebSocket webSocket, final Throwable t, Response response) {
            if (status == DISCONNECTED) {
                logd("连接已断开");
                if (isMainThread()) {
                    mainLooperHandler.syncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            webSocketStatusListener.onDisconnected(BREAK_ABNORMAL);
                        }
                    });
                } else {
                    webSocketStatusListener.onDisconnected(BREAK_ABNORMAL);
                }
                return;
            }

            loge("连接失败");
            reConnect();
            if (webSocketStatusListener != null) {
                if (isMainThread()) {
                    mainLooperHandler.syncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            webSocketStatusListener.onConnectionFailed(t);
                        }
                    });
                } else {
                    webSocketStatusListener.onConnectionFailed(t);
                }
            }
        }
    };

    private boolean isMainThread() {

        if (context.get() == null) {
            return false;
        }

        if (context.get() instanceof AbilitySlice) {
            if (((AbilitySlice) context.get()).getAbility().isTerminating()) {
                return false;
            }
            return true;
        }

        if (context.get() instanceof Ability) {
            AbilityInfo.AbilityType abilityType = context.get().getAbilityInfo().getType();
            if (abilityType == AbilityInfo.AbilityType.PAGE) {
                if (context.get() instanceof Ability) {
                    if (((Ability) context.get()).isTerminating()) {
                        return false;
                    }
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 设置是否自动重连
     *
     * @param autoReconnect autoReconnect
     * @return BaseWebSocket
     */
    public BaseWebSocket setAutoReconnect(boolean autoReconnect) {
        this.autoReconnect = autoReconnect;
        return this;
    }

    /**
     * 设置返回监听器
     *
     * @param webSocketStatusListener webSocketStatusListener
     * @return BaseWebSocket
     */
    public BaseWebSocket setWebSocketStatusListener(WebSocketStatusListener webSocketStatusListener) {
        this.webSocketStatusListener = webSocketStatusListener;
        return this;
    }

    /**
     * 返回重连次数
     *
     * @return int
     */
    public int getReconnectCount() {
        return reconnectCount;
    }

    private boolean isNetworkConnected(Context context) {
        NetHandle[] allNets = NetManager.getInstance(context).getAllNets();
        return null != allNets && allNets.length > 0;
    }

    /**
     * 获取当前连接状态
     *
     * @return int
     */
    public int getStatus() {
        return status;
    }

    private void logd(String s) {
        if (BaseOkHttpConfig.getInstance().isDebugMode()) {
            LockLog.logI(">>>", s);
        }
    }

    private void loge(String s) {
        if (BaseOkHttpConfig.getInstance().isDebugMode()) {
            LockLog.logE(">>>", s);
        }
    }

    /**
     * onDetach
     */
    public void onDetach() {
        context.clear();
    }
}
